package com.tskj.mh.savefoodorder.service;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.tskj.mh.savefoodorder.dao.MealsDAO;
import com.tskj.mh.savefoodorder.dao.redis.RedisUtil;
import com.tskj.mh.savefoodorder.dao.redis.RedissonDistributedLocker;
import com.tskj.mh.savefoodorder.utility.Tools;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * @author LeonSu
 */
@Service
public class SaveFoodOrderService {

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private MealsDAO mealsDAO;

    @Autowired
    private StudentService studentService;

    @Autowired
    private RedissonDistributedLocker distributedLocker;

    public String getAdUserName(String token) {
        Object value = redisUtil.get(token);
        if (value != null) {
            return value.toString();
        }
        return "";
    }

    public JSONObject saveOrder(String adUserName, JSONObject data) {
        JSONObject retData = new JSONObject();

        JSONObject student = studentService.getStudentInfo(adUserName);
        if (student == null) {
            retData.put("code", 2);
            retData.put("errMsg", "失败，缺少订餐人信息");
            return retData;
        }

        JSONObject redisData = new JSONObject();

        if (!data.containsKey("timeTypeId")) {
            retData.put("code", 2);
            retData.put("errMsg", "失败，缺少内容");
            return retData;
        }
        //TODO：先锁定订餐队列，然后判断有没有空位，
        //查询可用订单数
        String today = Tools.today();
        // 时间段id
        String timeId = data.getString("timeId");
        String key = "myLock_FoodOrder_" + today + "_" + timeId;
        try {
            // 尝试获取锁，等待1秒，自己获得锁后一直不解锁则10秒后自动解锁
            boolean isGetLock = distributedLocker.tryLock(key, TimeUnit.SECONDS, 1L, 10L);
            if (!isGetLock) {
                //没有可用订单
                retData.put("code", 1);
                retData.put("errMsg", "当前排队人数过多，请另选择取餐时间段\n不便之处，敬请见谅");
                return retData;
            }

            //TODO：获取锁成功后，先判断订单是否已满

            long emptyOrders = redisUtil.lGetListSize(today + "_" + timeId);
            if (emptyOrders <= 0) {
                //没有可用订单
                retData.put("code", 1);
                retData.put("errMsg", "当前时间订单已满，请另选择取餐时间段\n不便之处，敬请见谅");
                return retData;
            }

            // 餐段id
            String timeTypeId = data.getString("timeTypeId");
            // 食堂id
            String resId = data.getString("resId");
            // 时间段内容
            String timeString = data.getString("timeText");
            String startTime = timeString.split("-")[0];
            String endTime = timeString.split("-")[1];

            String currentTime = Tools.getNowTime();
            if (Tools.isAfter(currentTime,endTime + ":00")) {
                retData.put("code", 1);
                retData.put("errMsg", "超过预约截止时间，不能创建订单，请另选时间！！！");
                return retData;
            }

            //TODO：需要修改，从redis中获取
            JSONObject timeJson = mealsDAO.getTimeInfoFromRedis(timeId);
            String timeSortNo = timeJson.getInteger("time_rank_no").toString();
            if (timeSortNo.isEmpty()) {
                timeSortNo = mealsDAO.getTimeSortNo(timeId);
            }

            // 订餐总数量
            String mnum = data.getString("mnum");
            //订餐总金额
            String price = data.getString("price");
            JSONArray mealsList = data.getJSONArray("meals");
            String mealsInfo = "";

            //解析并合并套餐信息
            String canteenNo = "";
            String canteenName = "";
            for (int i = 0; i < mealsList.size(); i++) {
                JSONObject json = mealsList.getJSONObject(i);
                JSONObject mealsJson = mealsDAO.getMealInfo(json.getString("id")).getJSONObject(json.getString("id"));
                if (mealsJson != null) {
                    mealsInfo = mealsInfo.concat(String.format("%s（%s） ", mealsJson.getString("mealname"), json.getString("count")));
                    if (canteenNo.isEmpty()) {
                        canteenNo = mealsJson.getInteger("canteen_rank_no").toString();
                    }
                    if (canteenName.isEmpty()) {
                        canteenName = mealsJson.getString("canteenname");
                    }
                }
            }

            //如果redis中未获取到食堂的顺序号或食堂名称，则需要缓存穿透，从数据库中获取
            if (canteenNo.isEmpty() || canteenName.isEmpty()) {
                JSONObject canteenInfo = mealsDAO.getCanteenInfo(resId);
                canteenNo = canteenInfo.getInteger("canteen_rank_no").toString();
                canteenName = canteenInfo.getString("canteenname");
            }

            // 先保存订单

            //TODO：需要修改，从redis中获取最大订单号
            String orderNo = String.valueOf(Double.valueOf(mealsDAO.getMaxOrderNo(canteenName, today, startTime, endTime)).intValue());
            String code = String.format("%s-%s-%04d", canteenNo, timeSortNo, Integer.valueOf(orderNo));

            String studentName = student.getString("studentname");
            try {
                //保存到Redis中
                if (saveOrder(orderNo, studentName, canteenName, mealsInfo, mnum, price, startTime, endTime, code, today, adUserName)) {
                    // 再更新订单剩余数量
                    if (updateRedisOrders(today, resId, timeId, timeTypeId, emptyOrders - 1)) {
                        // 更新订单成功
                        retData.put("code", 0);
                        JSONObject detail = new JSONObject();
                        retData.put("data", detail);
                        JSONObject retStudent = new JSONObject();
                        retStudent.put("name", student.getString("username"));
                        retStudent.put("sex", student.getString("sexy"));
                        student.put("xueyuan", student.getString("xueyuan"));
                        detail.put("student", retStudent);

                        JSONObject order = new JSONObject();
                        order.put("type", 0);
                        order.put("address", canteenName);
                        order.put("date", today);
                        order.put("time", timeString);
                        order.put("count", mnum);
                        order.put("total", price);
                        order.put("code", Tools.Encrypt(code));
                        detail.put("order", order);
                    } else {
                        retData.put("code", 1);
                        retData.put("errMsg", "创建订单出现错误，请联系管理员");
                    }
                } else {
                    retData.put("code", 1);
                    retData.put("errMsg", "创建订单出现错误，请联系管理员");
                }
            } catch (Exception e) {
                e.printStackTrace();
                retData.put("code", 1);
                retData.put("errMsg", "创建订单出现错误，请联系管理员");
            }
        } finally {
            // 解锁
            distributedLocker.unlock(key);
        }
        return retData;
    }

    private boolean saveOrder(String orderNo, String studentName, String canteenName, String mealsInfo, String mnum, String price, String startTime, String endTime, String code, String today, String adUserName) throws Exception {
        mealsDAO.saveOrder(orderNo, studentName, canteenName, mealsInfo, mnum, price, startTime, endTime, code, today, adUserName);
        return true;
    }

    private boolean updateRedisOrders(String today, String resId, String timeId, String timeTypeId, long orders) {
        //TODO：需要共享锁
        // 先队列中少一个数
        redisUtil.leftPop(today + "_" + timeId);
        // 再计算当前剩余数
        Map<Object, Object> map = redisUtil.hmget(today);
        if (map != null) {
            HashMap<Object, Object> allTimeList = (HashMap<Object, Object>) map;
            Set<Object> keys = allTimeList.keySet();
            JSONObject time;
            for (Object key : keys) {
                JSONObject obj = JSONObject.parseObject(allTimeList.get(key).toString());
                if (obj.getString("resid").equals(resId) &&
                        obj.getString("timetypeid").equals(timeTypeId) &&
                        obj.getString("id").equals(timeId)) {
                    obj.put("used", orders);
                    map.put(key, obj);
                    break;
                }
            }
            redisUtil.hmset(today, map);
        }
        return true;
    }

}
